﻿using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WordNet.Core.ElementContracts;

namespace WordNet.Core
{
    public class Pointer:IPointer
    {
        public static readonly Pointer ALSO_SEE = new Pointer("^", "Also See", RelationType.Both);
        public static readonly Pointer ANTONYM = new Pointer("!", "Antonym",RelationType.Lexical);
        public static readonly Pointer ATTRIBUTE = new Pointer("=", "Attribute", RelationType.Semantic);
        public static readonly Pointer CAUSE = new Pointer(">", "Cause", RelationType.Semantic);
        public static readonly Pointer DERIVATIONALLY_RELATED = new Pointer("+", "Derivationally related form", RelationType.Both);
        public static readonly Pointer DERIVED_FROM_ADJ = new Pointer("\\", "Derived from adjective", RelationType.Lexical);
        public static readonly Pointer DOMAIN = new Pointer(";", "Domain of synset (undifferentiated)", RelationType.Both);
        public static readonly Pointer ENTAILMENT = new Pointer("*", "Entailment", RelationType.Semantic);
        public static readonly Pointer HYPERNYM = new Pointer("@", "Hypernym", RelationType.Semantic);
        public static readonly Pointer HYPERNYM_INSTANCE = new Pointer("@i", "Instance hypernym", RelationType.Semantic);
        public static readonly Pointer HYPONYM = new Pointer("~", "Hyponym", RelationType.Semantic);
        public static readonly Pointer HYPONYM_INSTANCE = new Pointer("~i", "Instance hyponym", RelationType.Semantic);
        public static readonly Pointer HOLONYM_MEMBER = new Pointer("#m", "Member holonym", RelationType.Semantic);
        public static readonly Pointer HOLONYM_SUBSTANCE = new Pointer("#s", "Substance holonym", RelationType.Semantic);
        public static readonly Pointer HOLONYM_PART = new Pointer("#p", "Part holonym", RelationType.Semantic);
        public static readonly Pointer MEMBER = new Pointer("-", "Member of this domain (undifferentiated)", RelationType.Both);
        public static readonly Pointer MERONYM_MEMBER = new Pointer("%m", "Member meronym",RelationType.Semantic);
        public static readonly Pointer MERONYM_SUBSTANCE = new Pointer("%s", "Substance meronym", RelationType.Semantic);
        public static readonly Pointer MERONYM_PART = new Pointer("%p", "Part meronym", RelationType.Semantic);
        public static readonly Pointer PARTICIPLE = new Pointer("<", "Participle", RelationType.Lexical);
        public static readonly Pointer PERTAINYM = new Pointer("\\", "Pertainym (pertains to nouns)", RelationType.Lexical);
        public static readonly Pointer REGION = new Pointer(";r", "Domain of synset - REGION", RelationType.Both);
        public static readonly Pointer REGION_MEMBER = new Pointer("-r", "Member of this domain - REGION", RelationType.Both);
        public static readonly Pointer SIMILAR_TO = new Pointer("&", "Similar To", RelationType.Semantic);
        public static readonly Pointer TOPIC = new Pointer(";c", "Domain of synset - TOPIC", RelationType.Both);
        public static readonly Pointer TOPIC_MEMBER = new Pointer("-c", "Member of this domain - TOPIC", RelationType.Both);
        public static readonly Pointer USAGE = new Pointer(";u", "Domain of synset - USAGE", RelationType.Both);
        public static readonly Pointer USAGE_MEMBER = new Pointer("-u", "Member of this domain - USAGE", RelationType.Both);
        public static readonly Pointer VERB_GROUP = new Pointer("$", "Verb Group", RelationType.Both);
       
        private static readonly Dictionary<String, Pointer> pointerMap;
        private static readonly HashSet<Pointer> pointerSet;
        
        private readonly String _symbol;
        private readonly String _name;
        private readonly String _toString;
        private readonly RelationType _relation;

        private const String ambiguousSymbol = "\\";
        static Pointer()
        {
            pointerMap = new Dictionary<string, Pointer>();
            pointerSet = new HashSet<Pointer>();
            SetDictionary();
        }
        
        private Pointer(String symbol, String name, RelationType relation)
        {
            _symbol = checkString(symbol);
            _name = checkString(name);
            _toString = name.ToLower().Replace(' ', '_').Replace(",", "");
            _relation = relation;
        }
        
        protected static String checkString(String str)
        {
            str = str.Trim();
            if (str.Length == 0)
                throw new ArgumentException();
            return str;
        }

        public RelationType Relation
        {
            get { return _relation; }
        }
       
        public string Symbol
        {
            get { return _symbol; }
        }

        public string Name
        {
            get { return _name; }
        }

        public override string ToString()
        {
            return _toString;
        }
       
        private static void SetDictionary()
        {
            FieldInfo[] fields = typeof(Pointer).GetFields(BindingFlags.Static | BindingFlags.Public);
            for (int i = 0; i < fields.Length; i++)
            {
                if (fields[i].FieldType == typeof(Pointer))
                {
                    var temp = (Pointer)fields[i].GetValue(null);
                    if (!pointerMap.ContainsKey(temp.Symbol))
                        pointerMap.Add(temp.Symbol, temp);
                }
            }
        }

        public static Pointer GetPointerType(String symbol, POS pos)
        {
            if (pos == POS.GetPartOfSpeech(DbPartOfSpeechType.Adv) && symbol.Equals(ambiguousSymbol))
                return DERIVED_FROM_ADJ;
            if (pos == POS.GetPartOfSpeech(DbPartOfSpeechType.Adj) && symbol.Equals(ambiguousSymbol))
                return PERTAINYM;
            Pointer pointerType = pointerMap[symbol];
            if (pointerType == null)
                throw new ArgumentException("No pointer type corresponding to symbol '" + symbol + "'");
            return pointerType;
        }

        public static IEnumerable<Pointer> values()
        {
            return pointerSet;
        }
    }
}
